package com.hruiworks.campusgroup.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hruiworks.campusgroup.algorithm.entity.TimePoint;
import com.hruiworks.campusgroup.algorithm.utils.OverLapCheckUtils;
import com.hruiworks.campusgroup.common.PageVO;
import com.hruiworks.campusgroup.dao.mapper.ActivityMapper;
import com.hruiworks.campusgroup.dao.mapper.GroupMapper;
import com.hruiworks.campusgroup.dao.mapper.JoinMapper;
import com.hruiworks.campusgroup.enums.ErrorCode;
import com.hruiworks.campusgroup.exception.BusinessException;
import com.hruiworks.campusgroup.manager.ActivityGroupManager;
import com.hruiworks.campusgroup.manager.entity.GroupFullInfo;
import com.hruiworks.campusgroup.pojo.entity.Activity;
import com.hruiworks.campusgroup.pojo.entity.Group;
import com.hruiworks.campusgroup.pojo.entity.Join;
import com.hruiworks.campusgroup.pojo.entity.User;
import com.hruiworks.campusgroup.pojo.query.GroupPageQuery;
import com.hruiworks.campusgroup.pojo.request.GroupCreateRequest;
import com.hruiworks.campusgroup.service.GroupService;
import com.hruiworks.campusgroup.service.NoticeService;
import com.hruiworks.usercheck.util.ParamUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.hruiworks.campusgroup.constants.redisKey.ActivityRedisKey.GROUP;

/**
 * @author jiushui
 * @description 针对表【group(团队表)】的数据库操作Service实现
 * @createDate 2025-03-07 09:28:24
 */
@Service
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group>
        implements GroupService {

    @Resource
    private GroupMapper groupMapper;

    @Resource
    private JoinMapper joinMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ActivityGroupManager activityGroupManager;

    @Resource
    private ActivityMapper activityMapper;

    @Resource
    private NoticeService noticeService;

    @Override
    @Transactional
    public Long createGroup(GroupCreateRequest groupCreateRequest, User user) {
        if (ParamUtils.isAnyBlank(groupCreateRequest)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在部分参数为空");
        }
        // 校验部分参数
        // 1. 需要人数需要大于1人
        if (groupCreateRequest.getNeededNum() < 2) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "需要人数需要大于1人");
        }
        // 2. 开始时间需要大于现在的时间，结束时间需要大于开始时间
        LocalDateTime expirationTime = groupCreateRequest.getExpirationTime();
        LocalDateTime startTime = groupCreateRequest.getStartTime();
        LocalDateTime endTime = groupCreateRequest.getEndTime();
        LocalDateTime now = LocalDateTime.now();

        if (startTime.isBefore(expirationTime) || startTime.isBefore(now) || endTime.isBefore(startTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "时间设置不对");
        }

        // 校验同时间段有没有参加其他的队伍
        // 需要判断同一时间段有没有加入其他的活动安排（排序+扫描）
        // 1. 先查询该用户加入了哪些团队
        LambdaQueryWrapper<Join> joinQueryWrapper = new LambdaQueryWrapper<>();
        joinQueryWrapper.eq(Join::getUserId, user.getId());
        List<Long> groupIdList = joinMapper.selectList(joinQueryWrapper).stream().map(Join::getGroupId).toList();
        if (!groupIdList.isEmpty()) {
            List<Group> groups = groupMapper.selectBatchIds(groupIdList);
            // 2. 将时间点取出来
            List<TimePoint> timePointList = new ArrayList<>();
            for (Group g : groups) {
                timePointList.add(new TimePoint(g.getStartTime(), true));
                timePointList.add(new TimePoint(g.getEndTime(), false));
            }
            timePointList.add(new TimePoint(groupCreateRequest.getStartTime(), true));
            timePointList.add(new TimePoint(groupCreateRequest.getEndTime(), false));
            // 3. 校验是否有重叠
            Boolean overLap = OverLapCheckUtils.isOverLap(timePointList, TimePoint::getPosition);
            if (BooleanUtils.isTrue(overLap)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "该时间段已有活动");
            }
        }


        Group group = new Group();
        BeanUtils.copyProperties(groupCreateRequest, group);
        group.setAlreadyNum(1);
        group.setCreateId(user.getId());
        BeanUtils.copyProperties(group, groupCreateRequest);
        int insertGroup = groupMapper.insert(group);
        if (insertGroup != 1) {
            throw new BusinessException(ErrorCode.OTHER_ERROR, "团队数据插入出问题");
        }
        Join join = new Join();
        join.setUserId(user.getId());
        join.setGroupId(group.getId());

        int insertJoin = joinMapper.insert(join);
        if (insertJoin != 1) {
            throw new BusinessException(ErrorCode.OTHER_ERROR, "加入数据插入出问题");
        }

        // 获取活动详情
        Activity activity = activityMapper.selectById(groupCreateRequest.getActivityId());
        // 活动开始通知
        LocalDateTime thirtyMinutesBeforeStart = startTime.minusMinutes(30);
        if (!now.isBefore(thirtyMinutesBeforeStart) && now.isBefore(startTime)) {
            noticeService.groupStartNoticeImmediately(user, group, activity);
        } else {
            noticeService.groupStartNoticeDefaultTime(user, group, activity);
        }

        return group.getId();
    }

    @Override
    @Transactional
    public Boolean joinGroup(User user, Long groupId) {
        RLock redissonClientLock = redissonClient.getLock(GROUP + groupId);
        try {
            if (!redissonClientLock.tryLock(0, 1, TimeUnit.MINUTES)) {
                throw new BusinessException(ErrorCode.FORBIDDEN, "加入失败");
            }
            Join join = new Join();
            join.setUserId(user.getId());
            join.setGroupId(groupId);

            // 待加入的团队
            Group group = groupMapper.selectById(groupId);

            // 需要判断同一时间段有没有加入其他的活动安排（排序+扫描）
            // 1. 先查询该用户加入了哪些团队
            LambdaQueryWrapper<Join> joinQueryWrapper = new LambdaQueryWrapper<>();
            joinQueryWrapper.eq(Join::getUserId, user.getId());
            List<Long> groupIdList = joinMapper.selectList(joinQueryWrapper).stream().map(Join::getGroupId).toList();
            if (!groupIdList.isEmpty()) {
                List<Group> groups = groupMapper.selectBatchIds(groupIdList);
                // 2. 将时间点取出来
                List<TimePoint> timePointList = new ArrayList<>();
                for (Group g : groups) {
                    timePointList.add(new TimePoint(g.getStartTime(), true));
                    timePointList.add(new TimePoint(g.getEndTime(), false));
                }
                timePointList.add(new TimePoint(group.getStartTime(), true));
                timePointList.add(new TimePoint(group.getEndTime(), false));
                // 3. 校验是否有重叠
                Boolean overLap = OverLapCheckUtils.isOverLap(timePointList, TimePoint::getPosition);
                if (BooleanUtils.isTrue(overLap)) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "该时间段已有活动");
                }
            }


            int insertJoin = joinMapper.insert(join);
            if (insertJoin != 1) {
                throw new BusinessException(ErrorCode.OTHER_ERROR, "加入数据插入出问题");
            }

            // 4. 更新团队已加入人数
            group.setAlreadyNum(group.getAlreadyNum() + 1);
            // 查看是否成团成功
            if (group.getAlreadyNum().equals(group.getNeededNum())) {
                group.setGroupStatus(1);
            }
            int updateGroup = groupMapper.updateById(group);
            if (updateGroup != 1) {
                throw new BusinessException(ErrorCode.OTHER_ERROR, "更新团队数据出问题");
            }

            // 获取活动详情
            Activity activity = activityMapper.selectById(group.getActivityId());

            // 人数变化通知其他成员
            LambdaQueryWrapper<Join> joinLambdaQueryWrapper = new LambdaQueryWrapper<>();
            joinLambdaQueryWrapper.eq(Join::getGroupId,groupId);
            List<Join> joins = joinMapper.selectList(joinLambdaQueryWrapper);
            List<Long> memberIdList = joins.stream().map(Join::getUserId).filter(id -> !id.equals(user.getId())).toList();

            String message = "你所参加的的"+activity.getActivityName()+"团队有人数变化："+group.getAlreadyNum()+"/"+group.getNeededNum();
            noticeService.newMemberNotice(user.getUniversity(), memberIdList, message);

            // 活动开始通知
            LocalDateTime startTime = group.getStartTime();
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime thirtyMinutesBeforeStart = startTime.minusMinutes(30);
            if (!now.isBefore(thirtyMinutesBeforeStart) && now.isBefore(startTime)) {
                noticeService.groupStartNoticeImmediately(user, group, activity);
            } else {
                noticeService.groupStartNoticeDefaultTime(user, group, activity);
            }


        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (redissonClientLock.isHeldByCurrentThread()) {
                redissonClientLock.unlock();
            }
        }


        return true;
    }

    @Override
    public PageVO<Group> listGroupPage(GroupPageQuery groupPageQuery) {
        LambdaQueryWrapper<Group> queryWrapper = groupPageQuery.baseLambdaQuery();

        Page<Group> page = this.page(new Page<>(groupPageQuery.getCurrent(), groupPageQuery.getSize()), queryWrapper);
        PageVO<Group> groupPageVO = new PageVO<>();
        groupPageVO.setRecords(page.getRecords());
        groupPageVO.setTotal(page.getTotal());
        groupPageVO.setSize(page.getSize());
        groupPageVO.setCurrent(page.getCurrent());

        return groupPageVO;
    }

    @Override
    public PageVO<Group> listGroupPageMain(GroupPageQuery groupPageQuery, User user) {

        // 先看活动是否存在或者已经过期
        LambdaQueryWrapper<Activity> existGroupWrapper = new LambdaQueryWrapper<>();
        existGroupWrapper.eq(Activity::getId, groupPageQuery.getActivityId());
        existGroupWrapper.gt(Activity::getExpirationTime, LocalDateTime.now());
        Activity activity = activityMapper.selectOne(existGroupWrapper);
        if (Objects.isNull(activity)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "活动不存在或者已经过期");
        }


        // 已经参加的团队id
        LambdaQueryWrapper<Join> joinLambdaQueryWrapper = new LambdaQueryWrapper<>();
        joinLambdaQueryWrapper.eq(Join::getUserId, user.getId());
        List<Long> groupIdList = joinMapper.selectList(joinLambdaQueryWrapper).stream().map(Join::getGroupId).toList();

        LambdaQueryWrapper<Group> queryWrapper = groupPageQuery.baseLambdaQuery();
        // 移除已参加的团队id
        if (!groupIdList.isEmpty()) {
            queryWrapper.notIn(Group::getId, groupIdList);
        }

        Page<Group> page = this.page(new Page<>(groupPageQuery.getCurrent(), groupPageQuery.getSize()), queryWrapper);
        PageVO<Group> groupPageVO = new PageVO<>();
        groupPageVO.setRecords(page.getRecords());
        groupPageVO.setTotal(page.getTotal());
        groupPageVO.setSize(page.getSize());
        groupPageVO.setCurrent(page.getCurrent());

        return groupPageVO;
    }

    @Override
    public PageVO<GroupFullInfo> listGroupPageSelf(Long size, Long current, Integer created, Integer time, User user) {
        if (ParamUtils.isAnyBlank(size, current, created, time)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "存在参数为空");
        }
        return activityGroupManager.listGroupFullInfoPageSelf(size, current, created, time, user);
    }

    @Override
    public void changeGroupStartNotice(User user, Long groupId, Long timeBeforehand) {
        Group group = groupMapper.selectById(groupId);
        noticeService.groupStartNoticeChangeTime(user, group, timeBeforehand, ChronoUnit.MINUTES);
    }


}




